#include "hinteger.h"
#include "stdio.h"
#include "test.h"
#include "math.h"

#define ISPRINT (1);
const int iteration = 10;

static void TestStr()
{
   Hinteger p;
   char bs[260];
   char os[260];
   char ds[260];
   char hs[260];

   p = 0x12345678ul;
   strcpy(bs, "b10010001101000101011001111000");
   strcpy(os, "02215053170");
   strcpy(ds, "305419896");
   strcpy(hs, "0x12345678");
   if (strcmp(p.b_str(), bs)) printf("%s != %s\n", p.b_str(), bs);
   if (strcmp(p.o_str(), os)) printf("%s != %s\n", p.o_str(), os);
   if (strcmp(p.d_str(), ds)) printf("%s != %s\n", p.d_str(), ds);
   if (strcmp(p.h_str(), hs)) printf("%s != %s\n", p.h_str(), hs);

   p = 0x12345678ul;
   p = (p << 32) + 0x9ABCDEF0ul;
   strcpy(bs, "b1001000110100010101100111100010011010101111001101111011110000");
   strcpy(os, "0110642547423257157360");
   strcpy(ds, "1311768467463790320");
   strcpy(hs, "0x123456789ABCDEF0");
   if (strcmp(p.b_str(), bs)) printf("%s != %s\n", p.b_str(), bs);
   if (strcmp(p.o_str(), os)) printf("%s != %s\n", p.o_str(), os);
   if (strcmp(p.d_str(), ds)) printf("%s != %s\n", p.d_str(), ds);
   if (strcmp(p.h_str(), hs)) printf("%s != %s\n", p.h_str(), hs);

   p = "b1001000110100010101100111100010011010101111001101111011110000";
   if (strcmp(p.b_str(), bs)) printf("%s != %s\n", p.b_str(), bs);
   p = "o110642547423257157360";
   if (strcmp(p.o_str(), os)) printf("%s != %s\n", p.o_str(), os);
   p = "1311768467463790320";
   if (strcmp(p.d_str(), ds)) printf("%s != %s\n", p.d_str(), ds);
   p = "0x123456789ABCDEF0";
   if (strcmp(p.h_str(), hs)) printf("%s != %s\n", p.h_str(), hs);
}

static void TestSh()
{
   Hinteger p;
   p = 123ul;
   p <<= 2;
   CHECK_EQ(p.d_str(), "492");
   p >>= 2;
   CHECK_EQ(p.d_str(), "123");
}

static void TestSign()
{
   Hinteger p, n, z;

   p = 1234567890ul;
   CHECK_EQ(p.d_str(), "1234567890");
   n = Hinteger(-1234567890l);
   CHECK_EQ(n.d_str(), "-1234567890");
   n = -1234567890l;
   CHECK_EQ(n.d_str(), "-1234567890");
   n = -p;
   CHECK_EQ(n.d_str(), "-1234567890");
   z = p + n;
   CHECK_EQ(z.d_str(), "0");
   n = p * n;
   CHECK_EQ(n.d_str(), "-1524157875019052100");
   n = n / p;
   CHECK_EQ(n.d_str(), "-1234567890");
}

static void TestMul()
{
   Hinteger p, n;
   p = "1234567890";
   n = "o777654321";
   p = p * n;
   CHECK_EQ(p.d_str(), "165648058986429810");
   p = "-1234567890";
   n = "1987654321";
   p = p * n;
   CHECK_EQ(p.d_str(), "-2453894201126352690");
   p = "1234567890";
   n = "-1987654321";
   p = p * n;
   CHECK_EQ(p.d_str(), "-2453894201126352690");
   p = "-1234567890";
   n = "-1987654321";
   p = p * n;
   CHECK_EQ(p.d_str(), "2453894201126352690");
}

static void TestDiv()
{
   Hinteger p, n;
   p = 128l;
   n = 8l;
   p = p / n;
   CHECK_EQ(p.d_str(), "16");
   p = "1524157875019052100";
   n = 1234567890l;
   p = p / n;
   CHECK_EQ(p.d_str(), "1234567890");
   p = -128l;
   n = 8l;
   p = p / n;
   CHECK_EQ(p.d_str(), "-16");
   p = "-1524157875019052100";
   n = 1234567890l;
   p = p / n;
   CHECK_EQ(p.d_str(), "-1234567890");
   p = 128l;
   n = -8l;
   p = p / n;
   CHECK_EQ(p.d_str(), "-16");
   p = "1524157875019052100";
   n = -1234567890l;
   p = p / n;
   CHECK_EQ(p.d_str(), "-1234567890");
   p = -128l;
   n = -8l;
   p = p / n;
   CHECK_EQ(p.d_str(), "16");
   p = "-1524157875019052100";
   n = -1234567890l;
   p = p / n;
   CHECK_EQ(p.d_str(), "1234567890");
}

static void TestRandomXor()
{
   Hinteger r, a, b;
   long rl, al, bl;
   for (int i=0; i<iteration; i++)
   {
      al = rand() * ((rand()%2)?(1):(-1));
      bl = rand() * ((rand()%2)?(1):(-1));

#ifndef ISPRINT
      printf("%i xor %i = %i\n", al, bl, al^bl);
#endif

      a = al;
      b = bl;

      r = a^b;
      rl = al^bl;

      CHECK_TRUE(r == rl);
   }
}

static void TestRandomOr()
{
   Hinteger r, a, b;
   long rl, al, bl;
   for (int i=0; i<iteration; i++)
   {
      al = rand() * ((rand()%2)?(1):(-1));
      bl = rand() * ((rand()%2)?(1):(-1));

#ifndef ISPRINT
      printf("%i or %i = %i\n", al, bl, al|bl);
#endif

      a = al;
      b = bl;

      r = a|b;
      rl = al|bl;

      CHECK_TRUE(r == rl);
   }
}

static void TestRandomAnd()
{
   Hinteger r, a, b;
   long rl, al, bl;
   for (int i=0; i<iteration; i++)
   {
      al = rand() * ((rand()%2)?(1):(-1));
      bl = rand() * ((rand()%2)?(1):(-1));

#ifndef ISPRINT
      printf("%i and %i = %i\n", al, bl, al&bl);
#endif

      a = al;
      b = bl;

      r = a&b;
      rl = al&bl;

      CHECK_TRUE(r == rl);
   }
}

static void TestRandomSum()
{
   Hinteger r, a, b;
   long rl, al, bl;
   for (int i=0; i<iteration; i++)
   {
      al = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));
      bl = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));

#ifndef ISPRINT
      printf("%i + %i\n", al, bl);
#endif

      a = al;
      b = bl;

      r = a+b;
#ifndef ISPRINT
      printf("%s + %s = %s\n", a.d_str(), b.d_str(), r.d_str());
#endif
      rl = al+bl;
#ifndef ISPRINT
      printf("%i + %i = %i\n", al, bl, rl);
#endif

      CHECK_TRUE(r == rl);
   }
}

static void TestRandomVin()
{
   Hinteger r, a, b;
   long rl, al, bl;
   for (int i=0; i<iteration; i++)
   {
      al = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));
      bl = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));

#ifndef ISPRINT
      printf("%i - %i\n", al, bl);
#endif

      a = al;
      b = bl;

      r = a-b;
      rl = al-bl;

      CHECK_TRUE(r == rl);
   }
}

static void TestRandomMul()
{
   Hinteger r, a, b;
   long rl, al, bl;
   for (int i=0; i<iteration; i++)
   {
      al = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));
      bl = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));

#ifndef ISPRINT
      printf("%i * %i\n", al, bl);
#endif

      a = al;
      b = bl;

      r = a*b;
      rl = al*bl;

      CHECK_TRUE(r == rl);
   }
}

static void TestRandomDiv()
{
   Hinteger r, a, b;
   long rl, al, bl;
   for (int i=0; i<iteration; i++)
   {
      al = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));
      bl = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));

#ifndef ISPRINT
      printf("%i / %i\n", al, bl);
#endif

      a = al;
      b = bl;

      r = a/b;
      rl = al/bl;

      CHECK_TRUE(r == rl);
   }
}

static void TestRandomMod()
{
   Hinteger r, a, b;
   long rl, al, bl;
   for (int i=0; i<iteration; i++)
   {
      al = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));
      bl = (rand()&0x7FFF) * ((rand()%2)?(1):(-1));

#ifndef ISPRINT
      printf("%i mod %i = %i\n", al, bl, al%bl);
#endif

      a = al;
      b = bl;

      r = a%b;
      rl = al%bl;

      CHECK_TRUE(r == rl);
   }
}

static void TestRandomSQRT()
{
   Hinteger r, a;
   long rl, al;
   for (int i=0; i<iteration; i++)
   {
      al = rand();
      rl = (long)sqrt((float)al);

#ifndef ISPRINT
      printf("%i sqrt %i \n", al, rl);
#endif

      a = al;
      r = a.sqrt();

      CHECK_TRUE(r == rl);
   }
}

void TestGiv()
{
   Hinteger a1, a2;
   a1 = a1 | Hinteger(200ul);
   a2 |= Hinteger(200ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 | Hinteger(12345ul);
   a2 |= Hinteger(12345ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 & Hinteger(98765ul);
   a2 &= Hinteger(98765ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 & Hinteger(34567ul);
   a2 &= Hinteger(34567ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 ^ Hinteger(23456ul);
   a2 ^= Hinteger(23456ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 ^ Hinteger(76543ul);
   a2 ^= Hinteger(76543ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 + Hinteger(24680ul);
   a2 += Hinteger(24680ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 + Hinteger(9753ul);
   a2 += Hinteger(9753ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 - Hinteger(1470ul);
   a2 -= Hinteger(1470ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 - Hinteger(963ul);
   a2 -= Hinteger(963ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 * Hinteger(357ul);
   a2 *= Hinteger(357ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 * Hinteger(963ul);
   a2 *= Hinteger(963ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 / Hinteger(124ul);
   a2 /= Hinteger(124ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 / Hinteger(347ul);
   a2 /= Hinteger(347ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 % Hinteger(98765ul);
   a2 %= Hinteger(98765ul);
   CHECK_TRUE(a1 == a2);

   a1 = a1 % Hinteger(347ul);
   a2 %= Hinteger(347ul);
   CHECK_TRUE(a1 == a2);
}

void TestHint()
{
   TestStr();
   TestSign();
   TestSh();
   TestMul();
   TestDiv();
   TestGiv();

   TestRandomXor();
   TestRandomOr();
   TestRandomAnd();
   TestRandomSum();
   TestRandomVin();
   TestRandomMul();
   TestRandomDiv();
   TestRandomMod();
   TestRandomSQRT();
}
